#include<iostream>
using namespace std;
#include<functional>
#include<unistd.h>

#include<pthread.h>

const static size_t THREAD_NUM = 4;
using func = function<void(const string& s)>;

void Func1(const string& s)
{
  cout << s << " is running" << endl;
}

void Func2(const string& s)
{
  cout << s << " is running" << endl;
}

void Func3(const string& s)
{
  cout << s << " is running" << endl;
}

void Func4(const string& s)
{
  cout << s << " is running" << endl;
}

struct ThreadData
{
  ThreadData(string threadName, pthread_cond_t* cond, pthread_mutex_t* mtx, func pf)
    : _threadName(threadName)
    , _cond(cond)
    , _mtx(mtx)
    , _pf(pf)
  {}


  string _threadName;
  pthread_cond_t* _cond;
  pthread_mutex_t* _mtx;
  func _pf;
};

bool quit = false;

void* CallBack(void* args)
{
  ThreadData* ptd = reinterpret_cast<ThreadData*>(args);
  while(1)
  {
    pthread_mutex_lock(ptd->_mtx); 
  
    pthread_cond_wait(ptd->_cond, ptd->_mtx);
    
    if(!quit)
    {
      ptd->_pf(ptd->_threadName);
    }
    else 
    {
      cout << ptd->_threadName << " quit" << endl;
      break;
    }
    
    pthread_mutex_unlock(ptd->_mtx);
  }

  delete ptd;
  ptd = nullptr;

  return nullptr;
}

int main()
{
  pthread_t threads[THREAD_NUM];

  func funcs[THREAD_NUM] = 
  {
    Func1,
    Func2,
    Func3,
    Func4
  };

  pthread_cond_t cond;
  pthread_cond_init(&cond, nullptr);

  pthread_mutex_t mtx;
  pthread_mutex_init(&mtx, nullptr);

  cout << sizeof(cond) << endl;
  cout << sizeof(mtx) << endl;

  for(size_t i = 0; i < THREAD_NUM; ++i)
  {
    ThreadData* ptd = new ThreadData(to_string(i + 1) + "号线程", &cond, &mtx, funcs[i]);
    pthread_create(&threads[i], nullptr, CallBack, ptd);
  }

  cout << "ready for start" << endl;

  sleep(1);

  cout << "start now" << endl;

  int count = 0;
  while(count != 8)
  {
    ++count;
    pthread_cond_signal(&cond);
    sleep(1);
  }
  quit = true;

  cout << "mission done" << endl;

  pthread_cond_destroy(&cond);
  pthread_mutex_destroy(&mtx);

  for(auto t : threads)
  {
    pthread_join(t, nullptr);
  }
  return 0;
}
